package com.keydom.ropeskipping;

import android.app.Activity;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.le.ScanRecord;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelUuid;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.clj.fastble.BleManager;
import com.clj.fastble.callback.BleGattCallback;
import com.clj.fastble.callback.BleScanCallback;
import com.clj.fastble.data.BleDevice;
import com.clj.fastble.exception.BleException;
import com.clj.fastble.scan.BleScanRuleConfig;
import com.keydom.ropeskipping.adapter.ScanItemAdapter;
import com.keydom.ropeskipping.utils.BluetoothUuid;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class ScanBleActivity extends Activity  {
    private String TAG = "ScanBleActivity";
    private ImageView imageLoading;
    private LinearLayout loadingLayout;
    private RecyclerView recyclerView;
    private ImageView imageResume;
    private RelativeLayout nullLayout;
    private TextView textLoadingHint;

    private RotateAnimation rotateAnimation;
    private ScanItemAdapter adapter;

    private static final int DATA_TYPE_FLAGS = 0x01;
    private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02;
    private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03;
    private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04;
    private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05;
    private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06;
    private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07;
    private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
    private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
    private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
    private static final int DATA_TYPE_SERVICE_DATA_16_BIT = 0x16;
    private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20;
    private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21;
    private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(0x80000000);
        setContentView(R.layout.activity_scan_ble);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        AppApplication.getInstance().addActivity(this);

        imageLoading = findViewById(R.id.image_loading);
        loadingLayout = findViewById(R.id.loading_layout);
        recyclerView = findViewById(R.id.recyclerView);
        imageResume = findViewById(R.id.image_resume);
        nullLayout = findViewById(R.id.null_layout);
        textLoadingHint = findViewById(R.id.text_loading_hint);

        rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        rotateAnimation.setDuration(2000);
        rotateAnimation.setRepeatCount(Animation.INFINITE);
        imageLoading.setAnimation(rotateAnimation);

        BleManager.getInstance().enableLog(true);
        BleScanRuleConfig scanRuleConfig = new BleScanRuleConfig.Builder()
//                .setServiceUuids(new UUID[]{UUID.fromString(BleManager.getInstance().UUID)})      // 只扫描指定的服务的设备，可选()
                .setScanTimeOut(10000)              // 扫描超时时间，可选，默认10秒；小于等于0表示不限制扫描时间
                .build();
        BleManager.getInstance().initScanRule(scanRuleConfig);
        BleManager.getInstance().disconnectAllDevice();

        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter = new ScanItemAdapter(R.layout.list_ble_item, new ArrayList<BleDevice>());
        recyclerView.setAdapter(adapter);

        imageResume.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                BleManager.getInstance().scan(new BleScanCallback() {
                    @Override
                    public void onScanStarted(boolean success) {
                        // 开始扫描设备
                        Log.e("蓝牙扫描-->", "开始扫描 ");
                        nullLayout.setVisibility(View.GONE);
                        imageLoading.setAnimation(rotateAnimation);
                        loadingLayout.setVisibility(View.VISIBLE);
                    }

                    @Override
                    public void onScanning(BleDevice result) {
                        Log.e("蓝牙扫描-->", "Name：" + result.getName() + "，RSSI：" + result.getRssi());
                        if (TextUtils.isEmpty(result.getName())) {
                            return;
                        }
                        List<ParcelUuid> uuids;
                        try {
                            uuids = parseFromBytes(result.getScanRecord());
                            if (uuids == null || uuids.size() == 0)  {
                                return;
                            }
                        } catch (Exception e) {
                            return;
                        }
                        for (ParcelUuid uuid : uuids) {
                            Log.e("蓝牙扫描-->", "UUID：" + uuid.getUuid().toString());
                        }
                        if (!AppApplication.getInstance().getBluetoothUuid().containsAll(uuids)) {
                            return;
                        }
                        loadingLayout.setVisibility(View.GONE);
                        if (!adapter.getData().contains(result)) {
                            adapter.getData().add(result);
                            adapter.notifyItemInserted(adapter.getData().size() - 1);
                            recyclerView.scrollToPosition(adapter.getData().size() - 1);
                        }
                    }

                    @Override
                    public void onScanFinished(List<BleDevice> scanResultList) {
                        // 扫描完成
                        Log.e("蓝牙扫描-->", "扫描完成 ");
                        if (adapter.getData().size() == 0) {
                            imageLoading.setAnimation(null);
                            loadingLayout.setVisibility(View.GONE);
                            nullLayout.setVisibility(View.VISIBLE);
                        }
                    }
                });
            }
        });

        imageResume.performClick();

        findViewById(R.id.title_back).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });

        adapter.setOnItemClickListener(new ScanItemAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(BleDevice bleDevice) {
                BleManager.getInstance().cancelScan();
                BleManager.getInstance().connect(bleDevice, new BleGattCallback() {
                    @Override
                    public void onStartConnect() {
                        // 开始连接
                        Log.e(TAG, "开始连接");
                        textLoadingHint.setText("跳绳连接中...");
                        imageLoading.setAnimation(rotateAnimation);
                        loadingLayout.setVisibility(View.VISIBLE);
                    }

                    @Override
                    public void onConnectFail(BleDevice bleDevice, BleException exception) {
                        // 连接失败
                        Log.e(TAG, "连接失败");
                        imageLoading.setAnimation(null);
                        textLoadingHint.setText("连接失败，请重试...");
                        imageLoading.setImageResource(R.mipmap.icon_connect_fail);
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                imageLoading.setImageResource(R.mipmap.icon_loading);
                                loadingLayout.setVisibility(View.GONE);
                            }
                        }, 2000);
                    }

                    @Override
                    public void onConnectSuccess(final BleDevice bleDevice, BluetoothGatt gatt, int status) {
                        // 连接成功，BleDevice即为所连接的BLE设备
                        Log.e(TAG, "连接成功");
                        imageLoading.setAnimation(null);
                        loadingLayout.setVisibility(View.GONE);
                        //更新设备时间
                        int currentTime = (int) (System.currentTimeMillis() / 1000);
                        Ble_ServiceAndChara(gatt);
                        BleManager.getInstance().write(bleDevice, "*{\"an\":\"ut\",\"t\":\"" + currentTime + "\"}#");

                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                Intent intent = new Intent(ScanBleActivity.this, WorkActivity.class);
                                intent.putExtra("data", getIntent().getSerializableExtra("data"));
                                startActivity(intent);
                                finish();
                            }
                        }, 200);
                    }

                    @Override
                    public void onDisConnected(boolean isActiveDisConnected, BleDevice bleDevice, BluetoothGatt gatt, int status) {
                        // 连接中断，isActiveDisConnected表示是否是主动调用了断开连接方法
                        Log.e(TAG, "连接中断");
                        sendBroadcast(new Intent("DIS_CONNECT_BLE"));
                    }
                });
            }
        });
    }

    @Override
    protected void onDestroy() {
        BleManager.getInstance().cancelScan();
        super.onDestroy();
    }

    private List<ParcelUuid> parseFromBytes(byte[] scanRecord) {
        if (scanRecord == null) {
            return null;
        }

        int currentPos = 0;
        int advertiseFlag = -1;
        List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
        String localName = null;
        int txPowerLevel = Integer.MIN_VALUE;

        SparseArray<byte[]> manufacturerData = new SparseArray<byte[]>();
        Map<ParcelUuid, byte[]> serviceData = new ArrayMap<ParcelUuid, byte[]>();

        try {
            while (currentPos < scanRecord.length) {
                // length is unsigned int.
                int length = scanRecord[currentPos++] & 0xFF;
                if (length == 0) {
                    break;
                }
                // Note the length includes the length of the field type itself.
                int dataLength = length - 1;
                // fieldType is unsigned int.
                int fieldType = scanRecord[currentPos++] & 0xFF;
                switch (fieldType) {
                    case DATA_TYPE_FLAGS:
                        advertiseFlag = scanRecord[currentPos] & 0xFF;
                        break;
                    case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL:
                    case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE:
                        parseServiceUuid(scanRecord, currentPos,
                                dataLength, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids);
                        break;
                    case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:
                    case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:
                        parseServiceUuid(scanRecord, currentPos, dataLength,
                                BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids);
                        break;
                    case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:
                    case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:
                        parseServiceUuid(scanRecord, currentPos, dataLength,
                                BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids);
                        break;
                    case DATA_TYPE_LOCAL_NAME_SHORT:
                    case DATA_TYPE_LOCAL_NAME_COMPLETE:
                        localName = new String(
                                extractBytes(scanRecord, currentPos, dataLength));
                        break;
                    case DATA_TYPE_TX_POWER_LEVEL:
                        txPowerLevel = scanRecord[currentPos];
                        break;
                    case DATA_TYPE_SERVICE_DATA_16_BIT:
                    case DATA_TYPE_SERVICE_DATA_32_BIT:
                    case DATA_TYPE_SERVICE_DATA_128_BIT:
                        int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
                        if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) {
                            serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT;
                        } else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) {
                            serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT;
                        }

                        byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
                                serviceUuidLength);
                        ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom(
                                serviceDataUuidBytes);
                        byte[] serviceDataArray = extractBytes(scanRecord,
                                currentPos + serviceUuidLength, dataLength - serviceUuidLength);
                        serviceData.put(serviceDataUuid, serviceDataArray);
                        break;
                    case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
                        // The first two bytes of the manufacturer specific data are
                        // manufacturer ids in little endian.
                        int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8)
                                + (scanRecord[currentPos] & 0xFF);
                        byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2,
                                dataLength - 2);
                        manufacturerData.put(manufacturerId, manufacturerDataBytes);
                        break;
                    default:
                        // Just ignore, we don't handle such data type.
                        break;
                }
                currentPos += dataLength;
            }

            if (serviceUuids.isEmpty()) {
                serviceUuids = null;
            }
            return serviceUuids;
        } catch (Exception e) {
            // As the record is invalid, ignore all the parsed results for this packet
            // and return an empty record with raw scanRecord bytes in results
//            return new ScanRecord(null, null, null, -1, Integer.MIN_VALUE, null, scanRecord);
            return null;
        }
    }

    // Parse service UUIDs.
    private int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength,
                                        int uuidLength, List<ParcelUuid> serviceUuids) {
        while (dataLength > 0) {
            byte[] uuidBytes = extractBytes(scanRecord, currentPos,
                    uuidLength);
            serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
            dataLength -= uuidLength;
            currentPos += uuidLength;
        }
        return currentPos;
    }

    // Helper method to extract bytes from byte array.
    private byte[] extractBytes(byte[] scanRecord, int start, int length) {
        byte[] bytes = new byte[length];
        System.arraycopy(scanRecord, start, bytes, 0, length);
        return bytes;
    }

    /**
     * 获取服务和特征值
     */
    private void Ble_ServiceAndChara(BluetoothGatt gatt) {
        List<BluetoothGattService> bluetoothGattServices = gatt.getServices();
        for (BluetoothGattService bluetoothGattService : bluetoothGattServices) {
            List<BluetoothGattCharacteristic> characteristics = bluetoothGattService.getCharacteristics();
            for (BluetoothGattCharacteristic characteristic : characteristics) {
                int charaProp = characteristic.getProperties();
                String uuid = characteristic.getUuid().toString().toLowerCase();
                if (uuid.startsWith("0000fd01")) {
                    BleManager.getInstance().WRITE_UUID = characteristic.getUuid().toString();
                    BleManager.getInstance().WRITE_SERVICE_UUID = bluetoothGattService.getUuid().toString();
                    Log.e(TAG, "write_UUID_chara=" + characteristic.getUuid());
                    Log.e(TAG, "write_UUID_service=" + bluetoothGattService.getUuid());
                }
                if (uuid.startsWith("0000fd02")) {
                    BleManager.getInstance().NOTIFY_UUID = characteristic.getUuid().toString();
                    BleManager.getInstance().NOTIFY_SERVICE_UUID = bluetoothGattService.getUuid().toString();
                    Log.e(TAG, "notify_UUID_chara=" + characteristic.getUuid());
                    Log.e(TAG, "notify_UUID_service=" + bluetoothGattService.getUuid());
                }
//                if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0 && (charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0) {
//                    BleManager.getInstance().WRITE_UUID = characteristic.getUuid().toString();
//                    BleManager.getInstance().WRITE_SERVICE_UUID = bluetoothGattService.getUuid().toString();
//                    Log.e(TAG, "write_UUID_chara=" + characteristic.getUuid());
//                    Log.e(TAG, "write_UUID_service=" + bluetoothGattService.getUuid());
//                }
//                if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0 && (charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
//                    BleManager.getInstance().NOTIFY_UUID = characteristic.getUuid().toString();
//                    BleManager.getInstance().NOTIFY_SERVICE_UUID = bluetoothGattService.getUuid().toString();
//                    Log.e(TAG, "notify_UUID_chara=" + characteristic.getUuid());
//                    Log.e(TAG, "notify_UUID_service=" + bluetoothGattService.getUuid());
//                }
            }
        }
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_HOME) {
            AppApplication.getInstance().exit();
            return true;
        }
        return super.onKeyUp(keyCode, event);
    }
}
